package com.yboot.starter.tenant.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yboot.starter.cache.config.RedisCacheManageAutoConfiguration;
import com.yboot.starter.cache.config.RedisCacheMissingBeanAutoConfiguration;
import com.yboot.starter.cache.core.redis.PrefixKeyStringRedisSerializer;
import com.yboot.starter.common.core.security.UserContext;
import com.yboot.starter.tenant.core.api.TenantIdApi;
import com.yboot.starter.tenant.core.aspect.TenantIgnoreAspect;
import com.yboot.starter.tenant.core.cache.TenantPrefixKeyStringRedisSerializer;
import com.yboot.starter.tenant.core.cache.TenantTimeoutRedisCacheManager;
import com.yboot.starter.tenant.core.mybatis.BaseTenantEntityInnerMetaObjectHandler;
import com.yboot.starter.tenant.core.mybatis.MybatisTenantHandler;
import com.yboot.starter.tenant.core.security.TenantContextFilter;
import com.yboot.starter.tenant.core.security.TenantSecurityHttpSecurityConfigurer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@AutoConfiguration
@AutoConfigureBefore({RedisCacheMissingBeanAutoConfiguration.class})
@ConditionalOnProperty(
    prefix = "business.tenant",
    value = {"enable"},
    havingValue = "true",
    matchIfMissing = true
)
@EnableConfigurationProperties({TenantProperties.class})
public class TenantAutoConfiguration {

    @Bean
    public TenantIgnoreAspect tenantIgnoreAspect() {
        return new TenantIgnoreAspect();
    }

    /**
     * 数据隔离-mybatis-plus
     */
    @Bean
    public BaseTenantEntityInnerMetaObjectHandler baseTenantEntityInnerMetaObjectHandler(TenantProperties tenantProperties,
                                                                                         UserContext userContext) {
        return new BaseTenantEntityInnerMetaObjectHandler(tenantProperties, userContext);
    }
    @Bean
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties,
                                                                 MybatisPlusInterceptor interceptor,
                                                                 UserContext userContext) {
        TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new MybatisTenantHandler(properties, userContext));
        List<InnerInterceptor> inners = new ArrayList(interceptor.getInterceptors());
        inners.add(0, inner);
        interceptor.setInterceptors(inners);
        return inner;
    }

    /**
     * 缓存隔离-redis的cache缓存,key生成隔离
     */
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory, RedisCacheConfiguration redisCacheConfiguration) {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        Map<String, RedisCacheConfiguration> initialCacheConfigurations = new HashMap();
        Map<String, Duration> cacheNameTtlMap = RedisCacheManageAutoConfiguration.getCacheNameTtlMap();
        cacheNameTtlMap.forEach((cacheName, ttl) -> {
            initialCacheConfigurations.put(cacheName, redisCacheConfiguration.entryTtl(ttl));
        });
        return new TenantTimeoutRedisCacheManager(redisCacheWriter, redisCacheConfiguration, initialCacheConfigurations, true);
    }
    @Bean
    public PrefixKeyStringRedisSerializer prefixKeyStringRedisSerializer() {
        return new TenantPrefixKeyStringRedisSerializer();
    }

    /**
     * security安全隔离租户
     */
    @Bean
//    public TenantSecurityHttpSecurityConfigurer tenantSecurityConfigurerAdapter(SecurityProperties securityProperties, TenantProperties tenantProperties, TenantIdApi tenantIdApi, ObjectMapper objectMapper) {
//        return new TenantSecurityHttpSecurityConfigurer(tenantProperties, tenantIdApi, objectMapper);
//    }
    public TenantContextFilter tenantContextFilter(TenantProperties tenantProperties,
                                                   TenantIdApi tenantIdApi,
                                                   ObjectMapper objectMapper) {
        TenantContextFilter filter = new TenantContextFilter(tenantIdApi, objectMapper);
        filter.ignoredAntPatterns(tenantProperties.getTenantFilterExcludes());
        return filter;
    }

    /**
     * websocket集成租户
     */
//    @Bean
//    public SaTokenChannelInterceptor saaTokenChannelInterceptor(PlatformSupportedUserDetailsService userDetailsService, StompPlatformSaTokenAuthenticationHandler stompPlatformSaTokenAuthenticationHandler, TenantIdApi tenantIdApi) {
//        return new TenantSaTokenChannelInterceptor(userDetailsService, stompPlatformSaTokenAuthenticationHandler, tenantIdApi);
//    }
}